package cn.itcast.tanhua.mytanhuadubboservice.api;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.PageUtil;
import cn.hutool.core.util.StrUtil;
import cn.itcast.tanhua.enums.CommentType;
import cn.itcast.tanhua.enums.IdType;
import cn.itcast.tanhua.mytanhuadubbointerface.api.VideoApi;
import cn.itcast.tanhua.pojo.Comment;
import cn.itcast.tanhua.pojo.FollowUser;
import cn.itcast.tanhua.pojo.Video;
import cn.itcast.tanhua.vo.PageInfo;
import cn.itcast.tanhua.vo.PageResult;
import com.alibaba.dubbo.config.annotation.Service;
import com.mongodb.client.result.DeleteResult;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.ArrayList;
import java.util.List;

/**
 * 关注用户功能
 *
 * @author 郭文亮
 * @since 2021/12/1 14:15
 */
@Service
public class VideoApiImpl implements VideoApi {
    /**
     * 定义redisKey的前缀
     */
    private static final String VIDEO_USER_ID_KEY_PREFIX = "VIDEO_USER_ID_";
    /**
     * 定义hashKey的前缀
     */
    private static final String VIDEO_FOLLOW_USER_ID_KEY_PREFIX = "VIDEO_FOLLOW_USER_ID_";

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private IdService idService;

    /**
     * 关注用户
     *
     * @param followUser 需要存储的信息
     * @return 返回存储的结果
     * @throws Exception 可能出现的异常
     */
    @Override
    public Boolean userFocus(FollowUser followUser) throws Exception {
        // 先判断userId和followId是否都有值，如果有一个没值，返回false
        if (!ObjectUtil.isAllNotEmpty(followUser.getUserId(), followUser.getFollowUserId())) {
            return false;
        }

        // 如果关注的用户和当前用户是同一个用户，返回false
        if (followUser.getUserId().longValue() == followUser.getFollowUserId().longValue()) {
            return false;
        }

        // 判断userId是否已经关注了followUserId,如果已经关注，返回false
        if (isFollowUser(followUser)) {
            return false;
        }
        // 将followUser存入MongoDB
        this.mongoTemplate.save(followUser);

        // 将关注结果存入redis
        String redisKey = getVideoUserIdKeyPrefix() + followUser.getUserId();
        String hashKey = getVideoFollowUserIdKeyPrefix() + followUser.getFollowUserId();
        this.redisTemplate.opsForHash().put(redisKey, hashKey, "1");
        return true;
    }

    /**
     * 取消关注用户
     *
     * @param followUser 关注用户的实体信息
     * @return 取消关注用户的结果
     * @throws Exception 可能出现的异常
     */
    @Override
    public Boolean userUnFocus(FollowUser followUser) throws Exception {
        // 先判断userId和followId是否都有值，如果有一个没值，返回false
        if (!ObjectUtil.isAllNotEmpty(followUser.getUserId(), followUser.getFollowUserId())) {
            return false;
        }

        // 如果取消关注的用户和当前用户是同一个用户，返回false
        if (followUser.getUserId().longValue() == followUser.getFollowUserId().longValue()) {
            return false;
        }

        // 判断userId是否已经关注了followUserId,如果未关注，返回false
        if (!isFollowUser(followUser)) {
            return false;
        }
        // 删除MongoDB的信息
        Query query = Query.query(Criteria.where("userId").is(followUser.getUserId())
                .and("followUserId").is(followUser.getFollowUserId()));
        DeleteResult deleteResult = this.mongoTemplate.remove(query, FollowUser.class);
        // 删除redis的信息
        if (deleteResult.getDeletedCount() > 0) {
            String redisKey = getVideoUserIdKeyPrefix() + followUser.getUserId();
            String hashKey = getVideoFollowUserIdKeyPrefix() + followUser.getFollowUserId();
            this.redisTemplate.opsForHash().delete(redisKey, hashKey);
            return true;
        }
        return false;
    }

    /**
     * 上传视频
     *
     * @param video 上传视频的实体信息
     * @return 上传视频的结果
     * @throws Exception 可能出现的异常
     */
    @Override
    public Boolean saveVideo(Video video) throws Exception {
        // 先判断userId、picUrl、videoUrl是否都有值，如果有一个没值，返回false
        if (!ObjectUtil.isAllNotEmpty(video.getUserId(), video.getPicUrl(), video.getVideoUrl())) {
            return null;
        }

        // 通过redis获取自增的vid
        video.setVid(this.idService.createId(IdType.VIDEO));

        // 将video存入MongoDB
        return this.mongoTemplate.save(video).getId() != null;

    }

    /**
     * 查询小视频
     * @param page 页码
     * @param pageSize 每页条数
     * @param userId 当前用户id
     * @return 查询结果
     * @throws Exception 可能出现的异常
     */
    @Override
    public PageInfo<Video> querySmallVideos(Integer page, Integer pageSize, Long userId,List<Long> blacklist) throws Exception {
        // 封装结果集pageInfo
        PageInfo<Video> pageInfo = new PageInfo<>();

        // 将page和pageSize封装到pageInfo
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);


        // 通过redis查询推荐的视频vids
        //从redis中获取推荐视频的数据
        String redisKey = "QUANZI_VIDEO_RECOMMEND_" + userId;
        String redisData = this.redisTemplate.opsForValue().get(redisKey);
        List<Long> vids = new ArrayList<>();
        int recommendCount = 0;
        if (StrUtil.isNotEmpty(redisData)) {
            //手动分页查询数据
            List<String> vidList = StrUtil.split(redisData, ',');

            //计算分页
            //[0, 10]
            int[] startEnd = PageUtil.transToStartEnd(page - 1, pageSize);
            int startIndex = startEnd[0]; //开始
            int endIndex = Math.min(startEnd[1], vidList.size()); //结束
            for (int i = startIndex; i < endIndex; i++) {
                vids.add(Convert.toLong(vidList.get(i)));
            }
            recommendCount = vidList.size();
        }

        if (CollUtil.isEmpty(vids)) {
            //没有推荐或前面推荐已经查询完毕，查询系统的视频数据

            //计算前面的推荐视频页数
            int totalPage = PageUtil.totalPage(recommendCount, pageSize);

            PageRequest pageRequest = PageRequest.of(page - totalPage - 1, pageSize, Sort.by(Sort.Order.desc("created")));
            Query query = new Query(Criteria.where("userId").nin(blacklist)).with(pageRequest);
            List<Video> videoList = this.mongoTemplate.find(query, Video.class);
            pageInfo.setRecords(videoList);
            return pageInfo;
        }


        //根据vid查询对应的视频数据了
        Query query = Query.query(Criteria.where("vid").in(vids));
        List<Video> videoList = this.mongoTemplate.find(query, Video.class);
        pageInfo.setRecords(videoList);

        return pageInfo;
    }

    @Override
    public Video queryVideoById(String videoId) {
        return this.mongoTemplate.findById(new ObjectId(videoId), Video.class);
    }

    /**
     * 判断是否已经关注用户
     *
     * @param followUser 包含userId和followUserId
     * @return 是否已经关注，true为已经关注，false表示未关注
     */
    private Boolean isFollowUser(FollowUser followUser) {
        String redisKey = getVideoUserIdKeyPrefix() + followUser.getUserId();
        String hashKey = getVideoFollowUserIdKeyPrefix() + followUser.getFollowUserId();
        return this.redisTemplate.opsForHash().hasKey(redisKey, hashKey);
    }

    /**
     * 封装redisKey的前缀
     *
     * @return redisKey的前缀
     */
    private String getVideoUserIdKeyPrefix() {
        return VIDEO_USER_ID_KEY_PREFIX;
    }

    /**
     * 封装hashKey的前缀
     *
     * @return hashKey的前缀
     */
    private String getVideoFollowUserIdKeyPrefix() {
        return VIDEO_FOLLOW_USER_ID_KEY_PREFIX;
    }

    /**
     * 查询数量
     *
     * @param publishId
     * @param commentType
     * @return
     */
    private Long queryCommentCount(String publishId, CommentType commentType) {
        Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId))
                .and("commentType").is(commentType.getType())
        );
        return this.mongoTemplate.count(query, Comment.class);
    }

    @Override
    public Long queryCommentCount(String publishId) {
        return this.queryCommentCount(publishId, CommentType.COMMENT);
    }
}
